home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / fish / 001-100 / 001-025 / 006 / scales / scales.c < prev    next >
C/C++ Source or Header  |  1995-03-17  |  18KB  |  597 lines

  1. /* SCALES
  2.  *
  3.  *   by Steven A. Bennett
  4.  *
  5.  *   This program demonstrates the use of the Audio functions
  6.  * in the "ROM" to produce four voice sound.  It uses a simple
  7.  * waveform (sawtooth) with no amplitude control (ie, envelope)
  8.  * or frequency variation (ie, vibrato), but these can easily be
  9.  * implemented through the use of setpv(), below.
  10.  * This program may be freely distributed.
  11.  *
  12.  * NOTES
  13.  *
  14.  *   - This program was written fairly quickly, without the aid
  15.  * of the ROM Kernal Manual (save about 10 pages of notes copied
  16.  * from my dealer's copy).  Therefore, if anyone sees anything too
  17.  * much out of the ordinary, bear with it.  (Particularly in mind
  18.  * is CreatePort(), of whose use I inferred from several programs
  19.  * I have downloaded from both BIX and CompuServe)  Furthermore, this
  20.  * particular implementation was written with a full blown music
  21.  * driver in mind, therefore, some code may seem ineffecient, for
  22.  * the sake of speed.  Finally, I tend to get a bit sloppy when i'm
  23.  * working past midnight, so there may be some messy areas.
  24.  *
  25.  * REVISIONS
  26.  *
  27.  *   12/25/85 - Single voice driver written. (SAB)
  28.  *   12/26/85 - Multi-voice capability added. (SAB)
  29.  *   12/28/85 - Error handling refined (see FinishProg()) (SAB)
  30.  *   12/30/85 - Fixed cleanup routine. (SAB)
  31.  *    1/10/86 - Mod to work with Both Manx and Lattice - crunch@well.UUCP
  32.  *
  33.  */
  34.  
  35. #include "exec/types.h"
  36. #include "exec/memory.h"
  37. #include "devices/audio.h"
  38. #include "stdio.h"
  39.  
  40. #define  PRIORITY          10L    /* Priority for Audio Channel usage */
  41. #define  NBR_IOA_STRUCTS   10L    /* Number of IOAudio structures used */
  42. #define  PV_IOA_STRUCT     0L     /* index to ioapv struct */
  43. #define  FIN_IOA_STRUCT    9L     /* index to finishioa struct */
  44. #define  BIG_WAVE          256L   /* size of biggest waveform */
  45. #define  NBR_WAVES         7L     /* number of waves per instrument */
  46. #define  WAVES_TOTAL       1024L  /* alloc size for instrument's waves */
  47. #define  YES               1L
  48. #define  NO                0L
  49.  
  50. extern struct MsgPort *CreatePort();
  51. extern void *AllocMem();
  52.  
  53. UBYTE aMap[] = { 0x0f };                  /* allocate four channels */
  54. long  voiceMap[] = { 1, 2, 4, 8 };
  55. struct IOAudio *ioa, *finishioa, *ioapv;
  56. struct IOAudio *ioainuse[4];
  57. struct IOAudio *freeioa[4];
  58. long unitno = 1;
  59. int error;
  60. int waiting[4] = { NO, NO, NO, NO };
  61. int woffsets[] =
  62.    { 0, 256, 384, 448, 480, 496, 504, 508, 510 };
  63. int wlen[] =
  64.    { 256, 128, 64, 32, 16, 8, 4, 2, 1 };
  65. int perval[] =
  66.    { 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226, 214 };
  67. BYTE *wptr;
  68. BYTE *owptr[4] = { NULL, NULL, NULL, NULL };
  69.  
  70. char *portstring[] = {
  71.    "Audio one",
  72.    "Audio two",
  73.    "Audio three",
  74.    "Audio four",
  75.    "Audio five",
  76.    "Audio six",
  77.    "Audio seven",
  78.    "Audio eight" };              /* names for the CreatePorts */
  79.  
  80. /* InitIOA()
  81.  *   This function initializes all IOAudio structures used by this
  82.  * program.
  83.  *
  84.  * NOTES
  85.  *   This version sets up enough IOAudio structures for four voices, plus
  86.  * a ADCMD_FINISH and an ADCMD_PERVOL, which are both synchronous.
  87.  * The ioainuse array is assumed to be the IOAudio structure which is
  88.  * being used by the current CMD_WRITE command for each voice.  The freeioa
  89.  * array is there so that no <click> is made when switching CMD_WRITEs.
  90.  * (ie., for speed)  Since the pointers are swapped when the switch occurs,
  91.  * one can always make the same assumption.  Note that ALL of the
  92.  * asynchronous (ie., ioainuse and freeioa) structures ought to have a
  93.  * unique ReplyPort (or so I think).  The IOAudio structure used to
  94.  * Open the Device must also have a ReplyPort, but it may not have to be
  95.  * unique.  I am taking no chances, however.
  96.  */
  97.  
  98. InitIOA()
  99.    {
  100.    int i;
  101.  
  102.    /* alloc the IOAudio structures
  103.     */
  104.    ioa = (struct IOAudio *)AllocMem((NBR_IOA_STRUCTS * (long)sizeof(*ioa)),
  105.       MEMF_PUBLIC | MEMF_CLEAR);
  106.    if (ioa == NULL)
  107.       FinishProg(1);
  108.  
  109.    /* set the various IOAudio structure pointers
  110.     */
  111.    for (i = 0; i < 4; ++i)
  112.       {
  113.       ioainuse[i] = &ioa[i + 1];
  114.       freeioa[i] = &ioa[i + 5];
  115.       }
  116.    ioapv = &ioa[PV_IOA_STRUCT];
  117.    finishioa = &ioa[FIN_IOA_STRUCT];
  118.  
  119.    /* Open the Audio Device.  This requires a ReplyPort, so we'll
  120.     * make it now, and reuse it later as part of ioapv, although it
  121.     * doesn't need one.  aMap[] is an array containing sets of
  122.     * bitmaps which allow various combinations of voices to be allocated.
  123.     * since we want all four voices, there is only one combination which
  124.     * will serve.
  125.     */
  126.    ioa->ioa_Request.io_Message.mn_Node.ln_Pri = PRIORITY;
  127.    ioa->ioa_Request.io_Message.mn_ReplyPort =
  128.       CreatePort("Audio zero", 0L);
  129.    if (ioa->ioa_Request.io_Message.mn_ReplyPort == NULL)
  130.       FinishProg(2);
  131.    ioa->ioa_Data = aMap;
  132.    ioa->ioa_Length = (long)sizeof(aMap);
  133.    error = OpenDevice(AUDIONAME, 0L, ioa, 0L);
  134.    if (error)
  135.       FinishProg(3);
  136.  
  137.    /* setup the finishioa and ioapv structs.  The IOF_QUICK flag
  138.     * makes them synchronous in all cases.
  139.     */
  140.    *finishioa = *ioa;
  141.    finishioa->ioa_Request.io_Flags = IOF_QUICK;
  142.    ioapv->ioa_Request.io_Flags = IOF_QUICK;
  143.  
  144.    finishioa->ioa_Request.io_Command = ADCMD_FINISH;
  145.    ioapv->ioa_Request.io_Command = ADCMD_PERVOL;
  146.  
  147.    /* setup the ioainuse and freeioa struct arrays.  All eight of
  148.     * them must have unique ReplyPorts, hence the CreatePort()s
  149.     * below.  portstring contains different port names for the
  150.     * eight ports.  (I have no idea as to what the zero in the
  151.     * CreatePort is used for, so don't ask.)
  152.     */
  153.    for (i = 0; i < 4; ++i)
  154.       {
  155.       *freeioa[i] = *ioa;
  156.       *ioainuse[i] = *ioa;
  157.       freeioa[i]->ioa_Request.io_Message.mn_ReplyPort =
  158.          CreatePort(portstring[i], 0L);
  159.       ioainuse[i]->ioa_Request.io_Message.mn_ReplyPort =
  160.          CreatePort(portstring[i + 4], 0L);
  161.       }
  162.    for (i = 0; i < 4; ++i)
  163.       if (freeioa[i]->ioa_Request.io_Message.mn_ReplyPort == NULL ||
  164.          ioainuse[i]->ioa_Request.io_Message.mn_ReplyPort == NULL)
  165.          FinishProg(4);
  166.    }
  167.  
  168. /* FinishProg(finishcode)
  169.  *    int finishcode;
  170.  *
  171.  *    displays an error message, if necessary, based on the value
  172.  * of finishcode, frees all allocated stuff (again based on finishcode)
  173.  * and then exits.
  174.  *
  175.  * NOTES
  176.  *   This is basically a tidy little "clean up" routine used by all exit
  177.  * routines in this program.  Since all the allocated "stuff" is supposed
  178.  * to be in global variables, freeing it up in one global location is
  179.  * much easier then doing it elsewhere.
  180.  *
  181.  */
  182.  
  183. char *errormsgs[] = {
  184.    "Finished!\n",
  185.    "Cannot allocate memory for IOAudio structures\n",
  186.    "Cannot create ReplyPort for OpenDevice call\n",
  187.    "Cannot open Audio Device\n",
  188.    "Cannot create ReplyPort(s) for remaining IOAudio structures\n",
  189.    "Cannot allocate memory for waveform\n",
  190.    "If you see this during execution, execute the programmer.\n" };
  191.  
  192. FinishProg(finishcode)
  193.    int finishcode;
  194.    {
  195.    int i;
  196.  
  197.    printf(errormsgs[finishcode]);
  198.    switch(finishcode)
  199.       {
  200.    case 0:
  201.       /* free up the WaveNode list
  202.        * (currently, just wptr)
  203.        */
  204.       FreeMem(wptr, WAVES_TOTAL);
  205.  
  206.    case 4:
  207.    case 5:
  208.       /* free up all ReplyPorts save the first.  Since we could be
  209.        * here due to an error in allocating same, we check first.
  210.        * (And yes, Virginia, we do fall through here)
  211.        */
  212.       for (i = 0; i < 4; ++i)
  213.          {
  214.          if (freeioa[i]->ioa_Request.io_Message.mn_ReplyPort)
  215.             DeletePort(freeioa[i]->ioa_Request.io_Message.mn_ReplyPort);
  216.          if (ioainuse[i]->ioa_Request.io_Message.mn_ReplyPort)
  217.             DeletePort(ioainuse[i]->ioa_Request.io_Message.mn_ReplyPort);
  218.          }
  219.  
  220.       /* Close the Audio Device
  221.        */
  222.       CloseDevice(ioa);
  223.  
  224.    case 3:
  225.       /* Delete the first ReplyPort
  226.        */
  227.       DeletePort(ioa->ioa_Request.io_Message.mn_ReplyPort);
  228.  
  229.    case 2:
  230.       /* Free the ioa memory
  231.        */
  232.       FreeMem(ioa, (NBR_IOA_STRUCTS * (long)sizeof(*ioa)));
  233.  
  234.       }
  235.    if (finishcode)
  236.       exit(1L);
  237.    exit(0L);
  238.    }
  239.  
  240. /* setwpv()
  241.  *
  242.  *   starts a sound on the channel specified by the global variable
  243.  * unitno.  By swapping between two IOAudio structures, this routine
  244.  * is able to accomplish it's task without a noticeable delay.
  245.  *
  246.  * NOTES
  247.  *   This routine should only be used when a waveform change is
  248.  * required, either due to frequency going out of the range of the
  249.  * existing waveform, or a different waveform has been chosen.  In
  250.  * most cases, setpv() will be sufficient.  Also, don't complain about
  251.  * the usage of io_Unit.  I didn't like it either.
  252.  *
  253.  */
  254.  
  255. setwpv(wf, len, per, vol, voice)
  256.    char *wf;
  257.    int len, per, vol, voice;
  258.    {
  259.    struct IOAudio *tmpioa;
  260.  
  261.    /* the next three lines are probably unnecessary and can be
  262.     * done instead in InitIOA, but why take chances?
  263.     */
  264.    freeioa[voice]->ioa_Request.io_Command = CMD_WRITE;
  265.    freeioa[voice]->ioa_Request.io_Flags = ADIOF_PERVOL | IOF_QUICK;
  266.    freeioa[voice]->ioa_Cycles = 0;
  267.  
  268.    /* Assign the unit numbers to the (<ahem>) pointer?
  269.     */
  270.    freeioa[voice]->ioa_Request.io_Unit = (struct Unit *)unitno;
  271.    finishioa->ioa_Request.io_Unit = (struct Unit *)unitno;
  272.  
  273.    /* Set the parameters
  274.     */
  275.    freeioa[voice]->ioa_Data = (UBYTE *)wf;
  276.    freeioa[voice]->ioa_Length = len;
  277.    freeioa[voice]->ioa_Period = per;
  278.    freeioa[voice]->ioa_Volume = vol;
  279.  
  280.    /* Terminate the old request, if there is one.  waiting[] is an
  281.     * boolean (well, pseudo-boolean) array stating if there was an
  282.     * old request.  The old CMD_WRITE must be finished, and the reply
  283.     * received, before the new CMD_WRITE will work.  I am not certain
  284.     * why this is necessary.  Originally, I tried a
  285.     *               BeginIO(freeioa[voice]);
  286.     *               BeginIO(finishioa);
  287.     *               WaitIO(ioainuse[voice]);
  288.     * series, but the net effect was that the freeioa request was
  289.     * ignored (without error), and the ioainuse request continued
  290.     * blindly onwards.  I had assumed that it would act as a FIFO queue,
  291.     * but apparently not.
  292.     */
  293.    if (waiting[voice])
  294.       {
  295.       BeginIO(finishioa);
  296.       WaitIO(ioainuse[voice]);
  297.       waiting[voice] = NO;
  298.       }
  299.  
  300.    /* now start up the new voice
  301.     */
  302.    BeginIO(freeioa[voice]);
  303.    error = CheckIO(freeioa[voice]);
  304.    if (error)
  305.       {
  306.       printf("Error on CMD_WRITE\n");
  307.       WaitIO(freeioa[voice]);
  308.       }
  309.    waiting[voice] = YES;
  310.  
  311.    /* swap the pointers.  That way, the next time we pass through, we
  312.     * will still work.
  313.     */
  314.    tmpioa = ioainuse[voice];
  315.    ioainuse[voice] = freeioa[voice];
  316.    freeioa[voice] = tmpioa;
  317.    }
  318.  
  319. /* setpv(per, vol)
  320.  *   int per, vol;
  321.  *
  322.  *   Changes the period and volume of the currently executing
  323.  * CMD_WRITE on the specified unitno.
  324.  *
  325.  * NOTES
  326.  *   This routine is perfect <ahem> as it is, both for single and
  327.  * multiple voices.  It can be used to change frequency, within limits,
  328.  * change volume, and simulate vibrato and envelope controls.
  329.  * For those of you wondering where I got my period values
  330.  * from, look in the ABASIC manual, page 138.  Or, if you wish, you
  331.  * can calculate it out for yourself using the formula:
  332.  *     period = C/(ns*hz)
  333.  *       - where C is the clock rate (3579545)
  334.  *               ns is the number of samples in a cycle of the wave
  335.  *               hz is the number of cycles per second.
  336.  * Thus, for middle A on the piano (440 hz) with 32 samples in the
  337.  * cycle of the waveform, one would get:
  338.  *     period = 3579545 / (32 * 440)
  339.  *            = 3579545 / 14080
  340.  *            = 254.229 (or pretty close)
  341.  * The period must be rounded to the nearest integer, which can result
  342.  * in a maximum frequency error of about .25%, assuming one uses the
  343.  * octave for frequency between period 226 and period 428.  (This comes
  344.  * out to be less than a twentieth step at the shortest period)
  345.  * period values of less than 127 are illegal, as there aren't enough
  346.  * cycles set aside for audio DMA for anything less.  period values of
  347.  * greater than 500 or so aren't recommended as the anti-aliasing
  348.  * filter isn't of much use then, and actually could cause a possible
  349.  * high pitched overtone, which I'm sure nobody wants.  Thus I am
  350.  * only going to use this to handle a single octave's range.
  351.  * Changes of octave are accomplished by doubling or halving the number
  352.  * of samples in one cycle of the waveform, and must, therefore, call
  353.  * setwpv().
  354.  */
  355.  
  356. setpv(per, vol)
  357.    int per, vol;
  358.    {
  359.    ioapv->ioa_Period = per;
  360.    ioapv->ioa_Volume = vol;
  361.    ioapv->ioa_Request.io_Unit = (struct Unit *)unitno;
  362.    BeginIO(ioapv);
  363.    }
  364.  
  365. /* StopVoices()
  366.  *
  367.  *   Terminates all CMD_WRITE routines in progress, effectively
  368.  * ending all sound.  Volume is set to zero first, just to be tidy.
  369.  *
  370.  */
  371.  
  372. StopVoices()
  373.    {
  374.    int voice;
  375.  
  376.    for (voice = 0; voice < 4; ++voice)
  377.       {
  378.       if (waiting[voice])
  379.          {
  380.          /* to stop a voice, we first set it's volume to zero
  381.           * (probably unnecessary) and then finish the CMD_WRITE.
  382.           */
  383.          unitno = voiceMap[voice];
  384.          setpv(128, 0);
  385.          finishioa->ioa_Request.io_Unit = (struct Unit *)unitno;
  386.          BeginIO(finishioa);
  387.          WaitIO(ioainuse[voice]);
  388.          waiting[voice] = NO;
  389.          }
  390.       }
  391.    }
  392.  
  393. /* setwave(wfp)
  394.  *   BYTE wfp;
  395.  *
  396.  *   this routine makes the first 256 bytes (lowest octave) of the
  397.  * sawtooth wave's waveform table.  wfp must have already been
  398.  * allocated and in CHIP MEMORY!!!  This is necessary later for the
  399.  * CMD_WRITE commands to work.
  400.  *   a sawtooth wave is a simple waveform, very easy to use.  It
  401.  * basically looks like this:
  402.  *
  403.  *     /|      /|      /|      /|
  404.  *    / |     / |     / |     / |
  405.  *   /  |    /  |    /  |    /  |
  406.  *  /   |   /   |   /   |   /   |
  407.  *      |  /    |  /    |  /    |  /
  408.  *      | /     | /     | /     | /
  409.  *      |/      |/      |/      |/
  410.  *
  411.  */
  412.  
  413. setwave(wfp)
  414.    UBYTE *wfp;          /* This is a sneaky way of making a sawtooth */
  415.    {
  416.    int i;
  417.  
  418.    for (i = 0; i < BIG_WAVE; ++i)
  419.       wfp[i] = i;
  420.    }
  421.  
  422. /* xpandwave(wfp)
  423.  *   BYTE *wfp;
  424.  *
  425.  * xpandwave expands a wave with only the base BIG_WAVE specified,
  426.  * into a set of NBR_WAVES waveforms, each for one octave.
  427.  * All of the waveforms are left in contiguous memory after the
  428.  * first wave, in order of decending sizes (256, 128, 64, ...)
  429.  *
  430.  * NOTES
  431.  *   Be forewarned that this function makes two assumptions.  The
  432.  * first is that there is enough memory in the wfp buffer to hold the
  433.  * expansion, and two, that the stuff is in chip mem.  (Actually,
  434.  * makewaves() makes that assumption instead.)  This function ought to
  435.  * work for any BIG_WAVE which is a power of two greater than 2^NBR_WAVES
  436.  *
  437.  */
  438.  
  439. xpandwave(wfp)
  440.    BYTE *wfp;
  441.    {
  442.    int i, j, rate;
  443.    BYTE *tptr;
  444.  
  445.    rate = 1;
  446.    tptr = wfp + BIG_WAVE;
  447.    for (i = 0; i < NBR_WAVES - 1; ++i)
  448.       {
  449.       rate *= 2;
  450.       for (j = 0; j < BIG_WAVE; j += rate)
  451.          *tptr++ = wfp[j];
  452.       }
  453.    }
  454.  
  455. /* makewaves()
  456.  *
  457.  *   just makes a sawtooth waveform in chip mem and expands it without
  458.  * the pretty list control and file IO stuff.
  459.  *
  460.  */
  461.  
  462. makewaves()
  463.    {
  464.    /* allocate the memory for the waveform.
  465.     */
  466.    wptr = (BYTE *)AllocMem(WAVES_TOTAL, MEMF_CHIP);
  467.    if (wptr == NULL)
  468.       FinishProg(5);
  469.  
  470.    /* get and expand the waveform
  471.     */
  472.    setwave(wptr);
  473.    xpandwave(wptr);
  474.    }
  475.  
  476. /* strike(note, voice)
  477.  *   int note, voice;
  478.  *
  479.  * This nice little routine takes a note and plays it on the given
  480.  * voice.  At a fixed amplitude.  The note is basically an integer from
  481.  * 0 to 11 (c to b) plus 12 per octave above the first and lowest.  It
  482.  * uses a pointer to the last waveform used by the voice specified to
  483.  * determine if it needs to call setwpv() or just setpv().  The waveform
  484.  * is used by adding an index (woffsets[]) dependant on the octave.
  485.  * the length of the waveform (in wlen[]) is likewise dependant on
  486.  * the octave.  Note that octaves start with zero, not one.
  487.  */
  488.  
  489. strike(note, voice)
  490.    int note, voice;
  491.    {
  492.    int per, oct, len;
  493.    BYTE *wfp;
  494.  
  495.    unitno = voiceMap[voice];
  496.    if (note >= 100)           /* play a rest. */
  497.       {
  498.       if (waiting[voice])
  499.          setpv(200, 0);
  500.       return;
  501.       }
  502.    oct = note / 12;
  503.    per = perval[note % 12];
  504.    wfp = wptr + woffsets[oct];
  505.  
  506.    /* if the waveform hasn't changed since the last strike,
  507.     * then only change the period.
  508.     */
  509.    if (wfp == owptr[voice])
  510.       setpv(per, 32);         /* fixed volume */
  511.    else
  512.       {
  513.       setwpv(wfp, wlen[oct], per, 32, voice);
  514.       owptr[voice] = wfp;
  515.       }
  516.    }
  517.  
  518. main()
  519.    {
  520.    int i, j;
  521.  
  522.    InitIOA();
  523.    makewaves();
  524.  
  525.    /* simple scale
  526.     */
  527.    for (i = 0; i < 24; ++i)
  528.       {
  529.       strike(i, 0);
  530.       Delay(10L);
  531.       }
  532.  
  533.    /* crossed scales
  534.     */
  535.    for (j = 48; i < 48; ++i, --j)
  536.       {
  537.       strike(i, 0);
  538.       strike(j, 1);
  539.       Delay(10L);
  540.       }
  541.  
  542.    /* crossed scales with lower in sync
  543.     */
  544.    for (j = 48; i < 72; ++i, --j)
  545.       {
  546.       strike(i, 0);
  547.       strike(i - 48, 1);
  548.       strike(j, 2);
  549.       Delay(10L);
  550.       }
  551.  
  552.    /* slow c chord...
  553.     */
  554.    strike(36, 0);
  555.    strike(100, 1);
  556.    strike(100, 2);
  557.    Delay(60L);
  558.    strike(40, 1);
  559.    Delay(60L);
  560.    strike(43, 2);
  561.    Delay(120L);
  562.  
  563.    /* ...and into a minor with the fourth voice
  564.     */
  565.    strike(46, 3);
  566.    Delay(200L);
  567.  
  568.    /* and two quick c chords to finish it up
  569.     */
  570.    restall();
  571.    Delay(20L);
  572.    cchord();
  573.    Delay(20L);
  574.    restall();
  575.    Delay(1L);
  576.    cchord();
  577.    Delay(200L);
  578.    StopVoices();
  579.    FinishProg(0);
  580.    }
  581.  
  582. restall()
  583.    {
  584.    int i;
  585.  
  586.    for (i = 0; i < 4; ++i)
  587.       strike(100, i);
  588.    }
  589.  
  590. cchord()
  591.    {
  592.    strike(36, 0);
  593.    strike(40, 1);
  594.    strike(43, 2);
  595.    strike(48, 3);
  596.    }
  597.